<?php

class Bookmarks_Object_List {

  //Type of list, can be: user or home
  protected $_listType;

	//Identifier of the list usually userId
  protected $_listId;

 
  
  /* allowed types of list */
  static protected $_allowedListTypes = array('user','tag');

  
  /* mapping with db list_type */
  static protected $_listTypesDbMapping = array('user' => 'u','tag' =>'t');

  /* allowed mode for listingPerBookmarkId */
  static protected $_allowedListingPerBookmarkIdModes = array('before','after');
  
  /* 
	definition of the list list table 
	This table contains all the bookmarks of the different lists
  */
  static protected $_dbBookmarkListTableName = 'list';
  //List concatenated id (type.id )
  static protected $_dbBookmarkListListIdCol = 'listCId';
  static protected $_dbBookmarkListBookmarkIdCol = 'bookmarkId';
  static protected $_dbBookmarkListprivateCol = 'private';

  
  /* need to put a limit to the number of pages that can be retrieved */
  static protected $_limitPage = 1000;
  
  
     /*
	__construct - public constructor of the class. It is defined by the type of list that is requested and the identifier of the list
	@param listType - set that can be: user or following
	@param id - identifier of the list, it is a digit that can be: userId
	*/
  public function __construct($listType,$id) {

 if(!in_array($listType,self::$_allowedListTypes)) {
	  	require_once('bookmarks/objects/Exception.php');
  	    throw new Bookmark_Object_Exception('Invalid List Type',15);
	}
	
	if($listType == 'tag') {
		if(!self::_isValidTag($id)) {
			require_once('bookmarks/objects/Exception.php');
			throw new Bookmark_Object_Exception('Invalid Tag: must be alphanumric and _ and less than 15 characters',15);				
		}
		//we change the identifier to lowercase
		$id = strtolower($id);
		
	} else {
		$validator = new Zend_Validate_Digits();
		if(!$validator->isValid($id)) {
			require_once('bookmarks/objects/Exception.php');
			throw new Bookmark_Object_Exception('Invalid Identifier ',15);	
		}
	} //end else
	
	$this->_listType = $listType;
	$this->_listId = $id;

  }	//end constructor
  
    //The usual getter
  public function getListType() {
	  return $this->_listType;	
  }
  public function getListId() {
	  return $this->_listId;	
  }
  protected function _getListCId() {
	return self::_getListCIdStatic($this->getListType(),$this->getListId());
  }  
  
  //We need a static version for the publish bookmark
  static protected function _getListCIdStatic($listType,$listId) {
     return self::$_listTypesDbMapping[$listType].$listId; 
  }

  //delete - delete the bookmark from the lists databases
  //@param void
  //@return int number of appearance of the bookmark that have been deleted on  the databases
  static public function deleteBookmark($oBookmark) {
  	if(!($oBookmark instanceof Bookmarks_Object_Bookmark)) {
  		require_once('bookmarks/objects/Exception.php');
  		throw new Bookmark_Object_Exception('invalid bookmark',60);
  	}
    $userId = (int) Zend_Registry::get('app_user')->getId();
	if($oBookmark->getUserId() != $userId) {
  		require_once('bookmarks/objects/Exception.php');
  		throw new Bookmark_Object_Exception('user can\'t delete bookmark',15);		
	}
	
	//we retrieve all the list databases and delete the bookmark in them
  	$dbs = self::_getAllBookmarkListDbs();
  	$bookmarkId = $oBookmark->getId();
  	$nbPresence = 0;
  	foreach($dbs as $num => $db) {
  		$n = $db->delete(self::$_dbBookmarkListTableName,$db->quoteInto(self::$_dbBookmarkListBookmarkIdCol.' = ?',$bookmarkId));	
  		$nbPresence += $n;
  	}
  	return $nbPresence;
  	
  }
 
  protected function _canViewPrivateBookmarks() {

	$userId = Zend_Registry::get('app_user')->getId();

	//user can see the private bookmarks if he/she own the list
	if($this->getListId() == $userId) return true;
	return false;
	
	

  } // end _canViewPrivateBookmarks()
  
  //This function publish the bookmark to the different lists  of the user (user list and home list)
  
  static public function publishBookmarkToUser($oBookmark) {


	//First we publish the bookmark in the user list
	$userId = $oBookmark->getUserId();
	$listCId = self::_getListCIdStatic('user',$userId);

	$db = self::_getListDbStatic($listCId);
	
	$data = array(
		self::$_dbBookmarkListListIdCol => $listCId,
		self::$_dbBookmarkListBookmarkIdCol => $oBookmark->getId(),
		self::$_dbBookmarkListprivateCol => $oBookmark->isPrivate(),
	);

Zend_Registry::get('dt_log')->log('publishing bookmark to the user list:'.serialize($data), Zend_Log::DEBUG);
	
	$db->insert(self::$_dbBookmarkListTableName,$data);
	
  }

  
 

	
  //This function publish the bookmark to the different tags lists
  //Only public bookmarks are published to the list
  
  static public function publishBookmarkToTags($oBookmark) {

	//basic data check
	if(!($oBookmark instanceof Bookmarks_Object_Bookmark)) {
		require_once('bookmarks/objects/Exception.php');
		throw new Bookmark_Object_Exception('invalid bookmark',70);
	}
	
	$isPrivate = $oBookmark->isPrivate();
	if($isPrivate) return;

	//We retrieve the list of tags
	$tags = $oBookmark->getTags();
	if(empty($tags)) return;
	

	foreach($tags as $num => $tag) {
		//First we publish the bookmark in the user list
		$userId = $oBookmark->getUserId();
		$listCId = self::_getListCIdStatic('tag',$tag);

		$db = self::_getListDbStatic($listCId);
		
		$data = array(
			self::$_dbBookmarkListListIdCol => $listCId,
			self::$_dbBookmarkListBookmarkIdCol => $oBookmark->getId(),
			self::$_dbBookmarkListprivateCol => $isPrivate,
		);

	Zend_Registry::get('dt_log')->log('publishing bookmark to the tag list:'.serialize($data), Zend_Log::INFO);
		
		$db->insert(self::$_dbBookmarkListTableName,$data);
	} //end foreach tag	


  }
	

  //This function publish the bookmark to the different tags lists
  //Only public bookmarks are published to the list
  
  static public function deleteBookmarkTags($oBookmark) {

	//basic data check
	if(!($oBookmark instanceof Bookmarks_Object_Bookmark)) {
		require_once('bookmarks/objects/Exception.php');
		throw new Bookmark_Object_Exception('invalid bookmark',70);
	}
	
	$isPrivate = $oBookmark->isPrivate();
	if($isPrivate) return;

	//We retrieve the list of tags
	$tags = $oBookmark->getTags();
	if(empty($tags)) return;
	
	$bookmarkId = $oBookmark->getId();
	
	
	$where = array();
	$where[0] = self::$_dbBookmarkListBookmarkIdCol.' = '.$bookmarkId;

	foreach($tags as $num => $tag) {
		//First we publish the bookmark in the user list
		$listCId = self::_getListCIdStatic('tag',$tag);
		$db = self::_getListDbStatic($listCId);

		$where[1] = $db->quoteInto(self::$_dbBookmarkListListIdCol.' = ?',$listCId);		
		

		

	Zend_Registry::get('dt_log')->log('deleting bookmark tags:'.serialize($data), Zend_Log::INFO);
		
		$db->delete(self::$_dbBookmarkListTableName,$where);
	} //end foreach tag	


  }//end deleteBookmarkTags
	
  
  //Object model when returned in the API
  public function asArray() {

  }
  


  static public function formatPage($page = 1) {
      if(empty($page)) $page = 1;
      $validator = new Zend_Validate_Digits();
      if(!$validator->isValid($page)) {
  		require_once('bookmarks/objects/Exception.php');
  		throw new Bookmark_Object_Exception('invalid input parameters',15);      	
  	  }
  	  if(self::$_limitPage < $page) {
  		require_once('bookmarks/objects/Exception.php');
  		throw new Bookmark_Object_Exception('page search is limited to '.self::$_limitPage.' pages',15);   	  	
  	  }
  	  
  	  if($page < 1 ) $page = 1;
  	  return $page;		
  }

  static public function formatPerPage($perPage = 20) {
      if(empty($perPage)) $perPage = 20;
      $validator = new Zend_Validate_Digits();
      if(!$validator->isValid($perPage)) {
  		require_once('bookmarks/objects/Exception.php');
  		throw new Bookmark_Object_Exception('invalid input parameters',15);      	
  	  }
  	  if($perPage < 1 ) $perPage = 1;
  	  if($perPage > 100 ) $perPage = 100;
  	  return $perPage;
  }
  
  
  
  //////////////////////listing string functionalities /////////////////////////
  
  
    /* listingPerPage() - used to retrieve the latest bookmarks on a particular list on a page format
  	@param perPage - number of bookmarks to return
  	@param page - num of the page to return
  	@return array of php bookmarks	 
  */
	
  public function listingPerPage($perPage = 20,$page = 1) {
   
Zend_Registry::get('dt_log')->log('per page:'.$perPage.'-'.isset($perPage).'-'.empty($perPage), Zend_Log::DEBUG);	       
      //We validate the inputs
 	  $page = self::formatPage($page);
 	  $perPage = self::formatPerPage($perPage);
 	  
  	  $db = $this->_getListDb();
	  
	  //select from database
	  $select = $db->select()->from(self::$_dbBookmarkListTableName)->limitPage($page, $perPage);
      //we select a particular list
	  $select->where($db->quoteInto(self::$_dbBookmarkListListIdCol ." = ?", $this->_getListCId()));
      
	  //we had a condition if the user is not allowed to view private bookmarks
	  if(!$this->_canViewPrivateBookmarks()) {
		$select->where($db->quoteInto(self::$_dbBookmarkListprivateCol ." = ?", false));	  
	  }
	  
	  //we order by bookmark id with more recent bookmarks first
	  $select->order(self::$_dbBookmarkListBookmarkIdCol.' DESC');	
	  
	  //We call the generic function to generate the list
	  return $this->_listingSelect($select);

  } //end listing
  
 


   /* listingPerBookmarkId() - used to retrieve the latest bookmarks on a particular list based on a previously given 
  	@param mode - before or after the given bookmarkId
  	@param page - num of the page to return
  	@param perPage - number of items to return
  	@return array of php bookmarks	 
  */
	
  public function listingPerBookmarkId($mode,$bookmarkId,$perPage = 20) {

    //We validate the inputs
 	$perPage = self::formatPerPage($perPage);
	  
	$validator = new Zend_Validate_Digits();
	if(!$validator->isValid($bookmarkId)) {
	  	require_once('bookmarks/objects/Exception.php');
  	    throw new Bookmark_Object_Exception('Invalid Bookmark Identifier',15);	
	}
	
	if(!in_array($mode,self::$_allowedListingPerBookmarkIdModes)) {
	  	require_once('bookmarks/objects/Exception.php');
  	    throw new Bookmark_Object_Exception('Invalid mode',19);		  
	}
	
Zend_Registry::get('dt_log')->log('listing list mode:'.$mode.'-'.$bookmarkId.'-'.$perPage, Zend_Log::DEBUG);	       

 	  
  	  $db = $this->_getListDb();
	  
	  //select from database
	  $select = $db->select()->from(self::$_dbBookmarkListTableName)->limit($perPage);
      //we select a particular list
	  $select->where($db->quoteInto(self::$_dbBookmarkListListIdCol ." = ?", $this->_getListCId()));

      //we gave the id limiter (this depends on mode)
	if('after' == $mode) {
		$select->where($db->quoteInto(self::$_dbBookmarkListBookmarkIdCol ." > ?", $bookmarkId));
		$select->order(self::$_dbBookmarkListBookmarkIdCol.' ASC');
		//Do we have to invert the results
		//tricks because if we don t invert we have the earliest bookmarks first
		$invert = true;
	} elseif('before' == $mode) {
		$select->where($db->quoteInto(self::$_dbBookmarkListBookmarkIdCol ." < ?", $bookmarkId));	  
		$select->order(self::$_dbBookmarkListBookmarkIdCol.' DESC');
		//tricks because if we don t invert we have the earliest bookmarks first
		$invert = false;
	}
	  
	  //we had a condition if the user is not allowed to view private bookmarks
	  if(!$this->_canViewPrivateBookmarks()) {
		$select->where($db->quoteInto(self::$_dbBookmarkListprivateCol ." = ?", false));	  
	  }
	  
	  //we order by bookmark id with more recent bookmarks first
	
	  
	  //We call the generic function to generate the list
	  return $this->_listingSelect($select,$invert);

  } //end listing
 

 
  
   /* _listingSelect() - used to retrieve the latest bookmarks on a particular list on a page format
  	@param select 
	@param invert for searching bookmarks after the givn one we are obliged to invert the results to have the latest first (db trick)
	@return array of php bookmarks, not bookmark objects but bookmark asArray
  */ 
  protected function _listingSelect($select,$invert = false) {
  	$db = $this->_getListDb();
	$res = $db->fetchAll($select->__toString());    	  

    if(empty($res)) return array();

	//We must reinterpret the rows so it is independant to the way it is stored
	$results = array();
	require_once('bookmarks/objects/bookmark.php');
	


	Zend_Registry::get('dt_log')->log('starting row:'.$rowNum, Zend_Log::DEBUG);
	
	foreach($res as $num => $row) {
		try{
			$bookmark = new Bookmarks_Object_Bookmark($row[self::$_dbBookmarkListBookmarkIdCol]);
			$results[] = $bookmark->asArray();
    	} catch(Bookmark_Object_Exception $e){
    			Zend_Registry::get('dt_log')->log('Issue bookmark not found:'.$row[self::$_dbBookmarkListBookmarkIdCol], Zend_Log::ERR);
    	}

	}
	
	if($invert) krsort($results);
	
	return $results; 
  }
  
  
  
  
  
   ////////////////////// end listing string functionalities ///////////////////////// 
  
  
    
  
  protected function _getListDb() {
  	return self::_getListDbStatic($this->_getListCId());
  }

   static protected function _getListDbStatic($listCId) {
		return Zend_Registry::get('dt_dbs')->getDb('bookmarks','list',$listCId);	
  }
  
   static protected function _getListDbNumStatic($listCId) {
		return Zend_Registry::get('dt_dbs')->getDbNum('bookmarks','list',$listCId);	
  }

  /*
  This function retrieves all the databases that are used by the list
  Usefull for deleting the bookmark when you don't know where it could have been stored
  @param void
  @return array of databases
  */
  static protected function _getAllBookmarkListDbs() {
  	return Zend_Registry::get('dt_dbs')->getAllDbs('bookmarks','list');
  }


  
  static protected function _isValidTag($tag) {
   	if(preg_match(  '/([^a-zA-z0-9_]+)/i'  ,  $tag)) return false;
	return true;
  }

  
  
} //end class